package br.net.woodstock.tickettoride.api.manager.common;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import br.net.woodstock.rockframework.domain.service.ServiceException;
import br.net.woodstock.rockframework.persistence.orm.QueryMetadata;
import br.net.woodstock.rockframework.utils.ConditionUtils;
import br.net.woodstock.tickettoride.api.core.common.AbstractCommonService;
import br.net.woodstock.tickettoride.api.core.utils.Filter;
import br.net.woodstock.tickettoride.api.core.utils.FilterHelper;
import br.net.woodstock.tickettoride.api.core.utils.JPQLHelper;
import br.net.woodstock.tickettoride.api.core.utils.RepositoryHelper;
import br.net.woodstock.tickettoride.api.manager.ApplicationService;
import br.net.woodstock.tickettoride.orm.Application;
import br.net.woodstock.tickettoride.orm.ApplicationManager;
import br.net.woodstock.tickettoride.orm.ApplicationManagerStatus;
import br.net.woodstock.tickettoride.orm.ApplicationPermission;
import br.net.woodstock.tickettoride.orm.ApplicationStatus;
import br.net.woodstock.tickettoride.orm.User;

public abstract class AbstractApplicationService extends AbstractCommonService implements ApplicationService {

	private static final long	serialVersionUID									= -3134799610709694287L;

	private static final String	JPQL_GET_ID_APPLICATION_BY_ALIAS					= "SELECT a.id FROM Application AS a WHERE a.alias = :alias";

	private static final String	JPQL_GET_APPLICATION_BY_ALIAS						= "SELECT a FROM Application AS a WHERE a.alias = :alias";

	private static final String	JPQL_LIST_APPLICATION_BY_NAME						= "SELECT a FROM Application AS a WHERE a.name LIKE :name ORDER BY a.name";

	private static final String	JPQL_LIST_APPLICATION_BY_ALIAS						= "SELECT a FROM Application AS a WHERE a.alias LIKE :alias ORDER BY a.name";

	private static final String	JPQL_LIST_APPLICATION_BY_DESCRIPTION				= "SELECT a FROM Application AS a WHERE a.description LIKE :description ORDER BY a.name";

	private static final String	JPQL_LIST_APPLICATION_BY_MANAGER_AND_NAME			= "SELECT a FROM ApplicationManager AS am JOIN am.user AS u JOIN am.application AS a WHERE u.id = :userId AND a.name LIKE :name ORDER BY a.name";

	private static final String	JPQL_LIST_APPLICATION_BY_MANAGER_AND_ALIAS			= "SELECT a FROM ApplicationManager AS am JOIN am.user AS u JOIN am.application AS a WHERE u.id = :userId AND a.alias LIKE :alias ORDER BY a.name";

	private static final String	JPQL_LIST_APPLICATION_BY_MANAGER_AND_DESCRIPTION	= "SELECT a FROM ApplicationManager AS am JOIN am.user AS u JOIN am.application AS a WHERE u.id = :userId AND a.description LIKE :description ORDER BY a.name";

	private static final String	JPQL_LIST_PERMITED_APPLICATION_BY_ID				= "SELECT s FROM ApplicationPermission AS p JOIN p.destination AS d JOIN p.source AS s WHERE d.id = :id";

	private static final String	JPQL_LIST_MANAGER_APPLICATION_BY_ID					= "SELECT u FROM ApplicationManager AS m JOIN m.user AS u JOIN m.application AS a WHERE a.id = :id";

	private static final String	JPQL_DELETE_APPLICATION_PERMISSION_BY_ID			= "DELETE FROM ApplicationPermission WHERE destination.id = :id";

	private static final String	JPQL_DELETE_APPLICATION_MANAGER_BY_ID				= "DELETE FROM ApplicationManager WHERE application.id = :id";

	private static final Filter	NAME_FILTER											= FilterHelper.getStartWith("NAME");

	private static final Filter	ALIAS_FILTER										= FilterHelper.getStartWith("ALIAS");

	private static final Filter	DESCRIPTION_FILTER									= FilterHelper.getStartWith("DESCRIPTION");

	@Override
	public Application getById(final Integer id) {
		try {
			return this.getSimpleRepository().get(new Application(id));
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Integer getIdByAlias(final String alias) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("alias", alias);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_GET_ID_APPLICATION_BY_ALIAS, parameters);
			return this.getSimpleQueryableRepository().getSingle(metadata);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Application getByAlias(final String alias) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("alias", alias);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_GET_APPLICATION_BY_ALIAS, parameters);
			return this.getSimpleQueryableRepository().getSingle(metadata);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void save(final Application application) {
		try {
			application.setStatus(ApplicationStatus.ACTIVE);
			this.getSimpleRepository().save(application);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void update(final Application application) {
		try {
			Application a = this.getSimpleRepository().get(application);
			a.setAlias(application.getAlias());
			a.setDescription(application.getDescription());
			a.setName(application.getName());
			a.setPath(application.getPath());
			a.setStatus(application.getStatus());
			this.getSimpleRepository().update(a);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void delete(final Integer id) {
		try {
			this.getSimpleRepository().delete(new Application(id));
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Collection<Application> listByFilter(final String filter) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			String hql = null;
			if (ConditionUtils.isNotEmpty(filter)) {
				if (AbstractApplicationService.ALIAS_FILTER.matches(filter)) {
					String alias = AbstractApplicationService.ALIAS_FILTER.getValue(filter);
					parameters.put("alias", JPQLHelper.getLikeValue(alias, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_ALIAS;
				} else if (AbstractApplicationService.DESCRIPTION_FILTER.matches(filter)) {
					String description = AbstractApplicationService.DESCRIPTION_FILTER.getValue(filter);
					parameters.put("description", JPQLHelper.getLikeValue(description, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_DESCRIPTION;
				} else if (AbstractApplicationService.NAME_FILTER.matches(filter)) {
					String name = AbstractApplicationService.NAME_FILTER.getValue(filter);
					parameters.put("name", JPQLHelper.getLikeValue(name, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_NAME;
				} else {
					parameters.put("name", JPQLHelper.getLikeValue(filter, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_NAME;
				}
			} else {
				parameters.put("name", JPQLHelper.getLikeValue(null, false));
				hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_NAME;
			}

			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(hql, parameters);
			return this.getSimpleQueryableRepository().getCollection(metadata).getResult();
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Collection<Application> listByManager(final Integer userId, final String filter) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			String hql = null;
			if (ConditionUtils.isNotEmpty(filter)) {
				if (AbstractApplicationService.ALIAS_FILTER.matches(filter)) {
					String alias = AbstractApplicationService.ALIAS_FILTER.getValue(filter);
					parameters.put("alias", JPQLHelper.getLikeValue(alias, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_MANAGER_AND_ALIAS;
				} else if (AbstractApplicationService.DESCRIPTION_FILTER.matches(filter)) {
					String description = AbstractApplicationService.DESCRIPTION_FILTER.getValue(filter);
					parameters.put("description", JPQLHelper.getLikeValue(description, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_MANAGER_AND_DESCRIPTION;
				} else if (AbstractApplicationService.NAME_FILTER.matches(filter)) {
					String name = AbstractApplicationService.NAME_FILTER.getValue(filter);
					parameters.put("name", JPQLHelper.getLikeValue(name, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_MANAGER_AND_NAME;
				} else {
					parameters.put("name", JPQLHelper.getLikeValue(filter, false));
					hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_MANAGER_AND_NAME;
				}
			} else {
				parameters.put("name", JPQLHelper.getLikeValue(null, false));
				hql = AbstractApplicationService.JPQL_LIST_APPLICATION_BY_MANAGER_AND_NAME;
			}

			parameters.put("userId", userId);

			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(hql, parameters);
			return this.getSimpleQueryableRepository().getCollection(metadata).getResult();
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Collection<Application> getPermiteds(final Integer applicationId) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("id", applicationId);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_LIST_PERMITED_APPLICATION_BY_ID, parameters);
			return this.getSimpleQueryableRepository().getCollection(metadata).getResult();
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public Collection<User> getManagers(final Integer applicationId) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("id", applicationId);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_LIST_MANAGER_APPLICATION_BY_ID, parameters);
			return this.getSimpleQueryableRepository().getCollection(metadata).getResult();
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void saveApplicationPermission(final Integer destination, final Integer[] sources) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("id", destination);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_DELETE_APPLICATION_PERMISSION_BY_ID, parameters);
			this.getSimpleQueryableRepository().executeUpdate(metadata);
			for (Integer source : sources) {
				ApplicationPermission permission = new ApplicationPermission();
				permission.setDestination(new Application(destination));
				permission.setComment("---");
				permission.setDate(new Date());
				permission.setSource(new Application(source));
				this.getSimpleRepository().save(permission);
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void saveApplicationManager(final Integer application, final Integer[] users) {
		try {
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("id", application);
			QueryMetadata metadata = RepositoryHelper.toQueryMetadata(AbstractApplicationService.JPQL_DELETE_APPLICATION_MANAGER_BY_ID, parameters);
			this.getSimpleQueryableRepository().executeUpdate(metadata);
			for (Integer user : users) {
				ApplicationManager manager = new ApplicationManager();
				manager.setApplication(new Application(application));
				manager.setStatus(ApplicationManagerStatus.ACTIVE);
				manager.setUser(new User(user));
				this.getSimpleRepository().save(manager);
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

}
